package com.nf.mvc.util;

import java.lang.annotation.Annotation;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Method;

import static com.nf.mvc.util.ClassUtils.cast;

/**
 * <h3>参考资料</h3>
 * <li><a href="https://github.com/spring-projects/spring-framework/wiki/Spring-Annotation-Programming-Model">spring注解处理模型</a></li>
 * <li><a href="https://blog.csdn.net/qq_42187215/article/details/129769784">spring MergeAnnotations的理解</a></li>
 */
public abstract class AnnotationUtils {
    public static <T> T getAttrValue(AnnotatedElement ele, Class<? extends Annotation> annoClass) {
        return getAttrValue(ele, annoClass, "value");
    }

    public static <T> T getAttrValue(AnnotatedElement ele, Class<? extends Annotation> annoClass, String attrName) {
        return getAttributeValue(ele, annoClass, attrName);
    }

    /**
     * 获取指定注解属性的值，当指定注解属性值不等于excludeValue时，以获取的注解属性值为准，否则以fallbackValue值为准
     *
     * @param ele           被注解的元素
     * @param annoClass     注解类
     * @param attrName      属性名
     * @param excludeValue  排除值
     * @param fallbackValue 后备值
     * @param <T>           注解属性的类型
     * @return 注解属性值
     */
    public static <T> T getAttrValue(AnnotatedElement ele, Class<? extends Annotation> annoClass, String attrName, T excludeValue, T fallbackValue) {
        T attrValue = getAttributeValue(ele, annoClass, attrName);
        return !excludeValue.equals(attrValue) ? attrValue : fallbackValue;
    }


    private static <T> T getAttributeValue(AnnotatedElement ele, Class<? extends Annotation> annoClass, String attrName) {
        Annotation anno = ele.getDeclaredAnnotation(annoClass);
        try {
            Method valueMethod = annoClass.getDeclaredMethod(attrName);
            return cast(valueMethod.invoke(anno));
        } catch (Exception e) {
            throw new IllegalStateException("获取注解:" + annoClass.getName() + " 的属性:[" + attrName + "]的值失败", e);
        }
    }

    public static boolean isPresent(AnnotatedElement ele, Class<? extends Annotation> annoClass) {
        return ele.isAnnotationPresent(annoClass);
    }

    private static void validateAnno(AnnotatedElement ele, Class<? extends Annotation> annoClass) {
        if (!ele.isAnnotationPresent(annoClass)) {
            throw new IllegalStateException("元素:" + ele + " 上没有注解:" + annoClass.getName());
        }
    }

}
